From: Jonas Dreßler Date: Thu, 31 Jul 2025 09:42:26 +0000 (+0200) Subject: swipeTracker: Port to use ClutterPanGesture X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~104^2^2~42 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=0c3a06046d57424c4ce6492a0a7ccb6a40b51ef9;p=gnome-shell.git swipeTracker: Port to use ClutterPanGesture The new ClutterGesture subclass ClutterPanGesture fits the SwipeTracker quite well. API wise the biggest difference to the TouchSwipeGesture it was using before is that ClutterPanGesture has its own internal event history to keep track of deltas and to calculate a velocity in the end. That means we need to give it slighty special treatment right now, but we'll improve that with the next commits. So replace the internal TouchSwipeGesture and make the SwipeTracker use ClutterPanGesture directly. Part-of: --- diff --git a/js/ui/swipeTracker.js b/js/ui/swipeTracker.js index d3df86ab3..4720ee10f 100644 --- a/js/ui/swipeTracker.js +++ b/js/ui/swipeTracker.js @@ -208,97 +208,6 @@ const TouchpadSwipeGesture = GObject.registerClass({ } }); -const TouchSwipeGesture = GObject.registerClass({ - Properties: { - 'distance': GObject.ParamSpec.double( - 'distance', null, null, - GObject.ParamFlags.READWRITE, - 0, Infinity, 0), - 'orientation': GObject.ParamSpec.enum( - 'orientation', null, null, - GObject.ParamFlags.READWRITE, - Clutter.Orientation, Clutter.Orientation.HORIZONTAL), - }, - Signals: { - 'begin': {param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE]}, - 'update': {param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE]}, - 'end': {param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE]}, - 'cancel': {param_types: [GObject.TYPE_UINT, GObject.TYPE_DOUBLE]}, - }, -}, class TouchSwipeGesture extends Clutter.GestureAction { - _init(allowedModes, nTouchPoints, thresholdTriggerEdge) { - super._init(); - this.set_n_touch_points(nTouchPoints); - this.set_threshold_trigger_edge(thresholdTriggerEdge); - - this._allowedModes = allowedModes; - this._distance = global.screen_height; - this._lastPosition = 0; - } - - get distance() { - return this._distance; - } - - set distance(distance) { - if (this._distance === distance) - return; - - this._distance = distance; - this.notify('distance'); - } - - vfunc_gesture_prepare(actor) { - if (!super.vfunc_gesture_prepare(actor)) - return false; - - if ((this._allowedModes & Main.actionMode) === 0) - return false; - - let time = this.get_last_event(0).get_time(); - let [xPress, yPress] = this.get_press_coords(0); - let [x, y] = this.get_motion_coords(0); - const [xDelta, yDelta] = [x - xPress, y - yPress]; - const swipeOrientation = Math.abs(xDelta) > Math.abs(yDelta) - ? Clutter.Orientation.HORIZONTAL : Clutter.Orientation.VERTICAL; - - if (swipeOrientation !== this.orientation) - return false; - - this._lastPosition = - this.orientation === Clutter.Orientation.VERTICAL ? y : x; - - this.emit('begin', time, xPress, yPress); - return true; - } - - vfunc_gesture_progress(_actor) { - let [x, y] = this.get_motion_coords(0); - let pos = this.orientation === Clutter.Orientation.VERTICAL ? y : x; - - let delta = pos - this._lastPosition; - this._lastPosition = pos; - - let time = this.get_last_event(0).get_time(); - - this.emit('update', time, -delta, this._distance); - - return true; - } - - vfunc_gesture_end(_actor) { - let time = this.get_last_event(0).get_time(); - - this.emit('end', time, this._distance); - } - - vfunc_gesture_cancel(_actor) { - let time = Clutter.get_current_event_time(); - - this.emit('cancel', time, this._distance); - } -}); - const ScrollGesture = GObject.registerClass({ Properties: { 'enabled': GObject.ParamSpec.boolean( @@ -467,47 +376,41 @@ export const SwipeTracker = GObject.registerClass({ this._history = new EventHistory(); this._reset(); + this._panGesture = new Clutter.PanGesture({ + min_n_points: params.allowDrag ? 1 : GESTURE_FINGER_COUNT, + max_n_points: params.allowDrag ? 0 : GESTURE_FINGER_COUNT, + }); + this._panGesture.connect('may-recognize', () => { + return (this._allowedModes & Main.actionMode) !== 0; + }); + this._panGesture.connect('recognize', this._beginPanGesture.bind(this)); + this._panGesture.connect('pan-update', this._updatePanGesture.bind(this)); + this._panGesture.connect('end', this._endPanGesture.bind(this)); + this._panGesture.connect('cancel', this._cancelPanGesture.bind(this)); + this.bind_property('enabled', this._panGesture, 'enabled', 0); + this.bind_property_full('orientation', + this._panGesture, 'pan-axis', + GObject.BindingFlags.SYNC_CREATE, + (bind, source) => [ + true, + source === Clutter.Orientation.HORIZONTAL + ? Clutter.PanAxis.X : Clutter.PanAxis.Y, + ], + null); + actor.add_action_full("SwipeTracker", Clutter.EventPhase.BUBBLE, this._panGesture); + this._touchpadGesture = new TouchpadSwipeGesture(allowedModes); - this._touchpadGesture.connect('begin', this._beginGesture.bind(this)); - this._touchpadGesture.connect('update', this._updateGesture.bind(this)); + this._touchpadGesture.connect('begin', this._beginTouchpadGesture.bind(this)); + this._touchpadGesture.connect('update', this._updateTouchpadGesture.bind(this)); this._touchpadGesture.connect('end', this._endTouchpadGesture.bind(this)); this.bind_property('enabled', this._touchpadGesture, 'enabled', 0); this.bind_property('orientation', this._touchpadGesture, 'orientation', GObject.BindingFlags.SYNC_CREATE); - this._touchGesture = new TouchSwipeGesture(allowedModes, - GESTURE_FINGER_COUNT, - Clutter.GestureTriggerEdge.AFTER); - this._touchGesture.connect('begin', this._beginTouchSwipe.bind(this)); - this._touchGesture.connect('update', this._updateGesture.bind(this)); - this._touchGesture.connect('end', this._endTouchGesture.bind(this)); - this._touchGesture.connect('cancel', this._cancelTouchGesture.bind(this)); - this.bind_property('enabled', this._touchGesture, 'enabled', 0); - this.bind_property('orientation', this._touchGesture, 'orientation', - GObject.BindingFlags.SYNC_CREATE); - this.bind_property('distance', this._touchGesture, 'distance', 0); - global.stage.add_action_full('swipe', Clutter.EventPhase.CAPTURE, this._touchGesture); - - if (params.allowDrag) { - this._dragGesture = new TouchSwipeGesture(allowedModes, 1, - Clutter.GestureTriggerEdge.AFTER); - this._dragGesture.connect('begin', this._beginGesture.bind(this)); - this._dragGesture.connect('update', this._updateGesture.bind(this)); - this._dragGesture.connect('end', this._endTouchGesture.bind(this)); - this._dragGesture.connect('cancel', this._cancelTouchGesture.bind(this)); - this.bind_property('enabled', this._dragGesture, 'enabled', 0); - this.bind_property('orientation', this._dragGesture, 'orientation', - GObject.BindingFlags.SYNC_CREATE); - this.bind_property('distance', this._dragGesture, 'distance', 0); - actor.add_action_full('drag', Clutter.EventPhase.CAPTURE, this._dragGesture); - } else { - this._dragGesture = null; - } - if (params.allowScroll) { this._scrollGesture = new ScrollGesture(actor, allowedModes); - this._scrollGesture.connect('begin', this._beginGesture.bind(this)); - this._scrollGesture.connect('update', this._updateGesture.bind(this)); + this._scrollGesture.connect('begin', this._beginTouchpadGesture.bind(this)); + this._scrollGesture.connect('update', this._updateTouchpadGesture.bind(this)); this._scrollGesture.connect('end', this._endTouchpadGesture.bind(this)); this.bind_property('enabled', this._scrollGesture, 'enabled', 0); this.bind_property('orientation', this._scrollGesture, 'orientation', @@ -577,28 +480,32 @@ export const SwipeTracker = GObject.registerClass({ _interrupt() { this.emit('end', 0, this._cancelProgress); + this._panGesture.cancel(); this._reset(); } - _beginTouchSwipe(gesture, time, x, y) { - if (this._dragGesture) - this._dragGesture.cancel(); - - this._beginGesture(gesture, time, x, y); - } - - _beginGesture(gesture, time, x, y) { + _beginGesture(x, y) { if (this._state === State.SCROLLING) return; - this._history.append(time, 0); - const rect = new Mtk.Rectangle({x, y, width: 1, height: 1}); let monitor = global.display.get_monitor_index_for_rect(rect); this.emit('begin', monitor); } + _beginPanGesture(panGesture) { + const coords = panGesture.get_begin_centroid(); + + this._beginGesture(coords.x, coords.y); + } + + _beginTouchpadGesture(gesture, time, x, y) { + this._history.append(time, 0); + + this._beginGesture(x, y); + } + _findClosestPoint(pos) { const distances = this._snapPoints.map(x => Math.abs(x - pos)); const min = Math.min(...distances); @@ -645,27 +552,40 @@ export const SwipeTracker = GObject.registerClass({ return [this._snapPoints[lowerIndex], this._snapPoints[upperIndex]]; } - _updateGesture(gesture, time, delta, distance) { + _updateGesture(delta, distance) { if (this._state !== State.SCROLLING) return; - if ((this._allowedModes & Main.actionMode) === 0 || !this.enabled) { - this._interrupt(); - return; - } - if (this.orientation === Clutter.Orientation.HORIZONTAL && Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) delta = -delta; this._progress += delta / distance; - this._history.append(time, delta); this._progress = Math.clamp(this._progress, ...this._getBounds(this._initialProgress)); this.emit('update', this._progress); } + _updatePanGesture(panGesture) { + const deltaVec = panGesture.get_delta(); + const delta = this.orientation === Clutter.Orientation.HORIZONTAL + ? -deltaVec.get_x() + : -deltaVec.get_y(); + + this._updateGesture(delta, this._distance); + } + + _updateTouchpadGesture(gesture, time, delta, distance) { + if ((this._allowedModes & Main.actionMode) === 0 || !this.enabled) { + this._interrupt(); + return; + } + + this._history.append(time, delta); + this._updateGesture(delta, distance); + } + _getEndProgress(velocity, distance, isTouchpad) { if (this._cancelled) return this._cancelProgress; @@ -698,15 +618,7 @@ export const SwipeTracker = GObject.registerClass({ return this._snapPoints[index]; } - _endTouchGesture(_gesture, time, distance) { - this._endGesture(time, distance, false); - } - - _endTouchpadGesture(_gesture, time, distance) { - this._endGesture(time, distance, true); - } - - _endGesture(time, distance, isTouchpad) { + _endGesture(velocity, distance, isTouchpad) { if (this._state !== State.SCROLLING) return; @@ -715,9 +627,6 @@ export const SwipeTracker = GObject.registerClass({ return; } - this._history.trim(time); - - let velocity = this._history.calculateVelocity(); const endProgress = this._getEndProgress(velocity, distance, isTouchpad); velocity /= distance; @@ -736,12 +645,33 @@ export const SwipeTracker = GObject.registerClass({ this.emit('end', duration, endProgress); } - _cancelTouchGesture(_gesture, time, distance) { + _endPanGesture(panGesture) { + const velocity = panGesture.get_velocity(); + const v = this.orientation === Clutter.Orientation.HORIZONTAL + ? -velocity.get_x() + : -velocity.get_y(); + + this._endGesture(v, this._distance, false); + } + + _endTouchpadGesture(gesture, time, distance) { + if ((this._allowedModes & Main.actionMode) === 0 || !this.enabled) { + this._interrupt(); + return; + } + + this._history.trim(time); + const velocity = this._history.calculateVelocity(); + + this._endGesture(velocity, distance, true); + } + + _cancelPanGesture() { if (this._state !== State.SCROLLING) return; this._cancelled = true; - this._endGesture(time, distance, false); + this._endGesture(0, this._distance, false); } /** @@ -773,9 +703,9 @@ export const SwipeTracker = GObject.registerClass({ delete this._touchpadGesture; } - if (this._touchGesture) { - global.stage.remove_action(this._touchGesture); - delete this._touchGesture; + if (this._panGesture) { + this._panGesture.actor?.remove_action(this._panGesture); + delete this._panGesture; } } });